
----------------------------------------------------------------
--- Copyright：  迷你玩-海马
--- Description：数据缓存服务器 data_cache（简称DC）
---  注意：本模块目前仅支持 华为云 GaussDB for redis
----------跨服务器的GaussDB for redis redis数据cache层----------
----------将数据从逻辑服中分离出来，并提供共享内存加速----------
----------------------------------------------------------------
---  1 key-hash value 的数据结构：
---		key 100002
---			+ hash=1    value=string1
---			+ hash=2    value=string3
---			+ hash=3    value=string3
---
---	 2 key value的数据结构(可选带有expire_time有效期的排重数据)：
---		 key 10001_type1_222  value=string1  [可选]expire_time=86400*90(90天有效)
---		 key 10002_type2_123  value=string2  [可选]expire_time=86400*30(30天有效)
---     (key类型别用纯数字，用数据+类型组成字符串来使用)
----------------------------------------------------------------
---API接口
---try_lock_kh    主动加锁(key+hash 远程锁)(等待拿到锁，或者超时)
---unlock_kh      主动解锁(key+hash 远程锁)

---read    读取数据 (默认加远程锁 可选不加)   key+hash  get value
---save    保存数据 (默认解远程锁 可选不解)   key+hash  set value
---del     删除数据 (别名hdel)                key+hash  del value
---mread   批量读取数据                      (key1+hash1 key2+hash2 key3+hash3)

---hlen           按key获得key下的所有hash个数
---hkeys          按key获取所有hash值，不带有数据
---hget_all       按key获取所有hash值和数据（别名hgetall）

---scan           分段扫描使用(从0开始，每一次传入上次返回的游标值)
---clean_cache    直接清理cache

---set_kv         保存kv数据，不带hash ( 有时间限制的数据，一般0到366天，主要用来排重 )
---get_kv         获得kv数据，不带hash
---del_kv         删除kv数据，不带hash ( 暂未实现，可set_kv expire_time=0.001 数据立即过期 )

---comm    测试用的复合接口
---comm2   测试接口
----------------------------------------------------------------



local assert     = assert
local require    = require
local bit        = bit
local coroutine  = coroutine
local debug      = debug
local io         = io
local pairs      = pairs
local ipairs     = ipairs
local math       = math
local os         = os
local print      = print
local pcall      = pcall
local xpcall     = xpcall
local rawget     = rawget
local rawset     = rawset
local select     = select
local string     = string
local table      = table
local tonumber   = tonumber
local tostring	 = tostring
local error      = error
local type       = type
local unpack     = unpack
local setmetatable = setmetatable
local getmetatable = getmetatable
local ngx = ngx

local string_char  = string.char
local string_sub   = string.sub
local string_byte  = string.byte
local string_gsub  = string.gsub
local string_find  = string.find
local string_upper = string.upper

local math_fmod   = math.fmod
local math_floor  = math.floor
local math_pow    = math.pow
local math_abs    = math.abs


local uu           = require( "modules.commhm.utils" )
local log          = require( "modules.commhm.log" )
local ns_time      = require( "modules.commhm.time" )
local ns_network   = require( "modules.commhm.network" )
local ns_http      = require( "modules.commhm.resty.http" )

local ns_const_vars  = require("modules.commhm.const_vars")

local ns_config       = require( "modules.data_cache.data_cache_config" )
local gg              = require( "modules.data_cache.data_cache_redis_gg" )
local ns_comm_gg      = require( "modules.data_cache.comm_gg" )
local ns_auth_checker = require( "modules.data_cache.auth_checker" )


local ns_share_dict_checker = require("modules.commhm.framework.share_dict_checker");


local FOO = {
	_VERSION = '0.12.1',
	cmds = {},

	worker_id = ngx.worker.id(),

	DOUBLE_CHECK_RET = 1,   ---是否二次确认结果正确性 kh值
};



FOO.enter = function()
	ngx.ctx.m_params = ngx.req.get_uri_args();

	ngx.header["Carry"] = ngx.ctx.m_params.carry   --防止网络请求串流

	--log.debug( "headers1= " .. table.tostring(ngx.req.get_headers()) );

	local cmd_  = ngx.ctx.m_params.cmd
	if  cmd_  and FOO.cmds[cmd_] then
		if  ns_auth_checker.check_data_cache_auth( cmd_ ) then
			gg.init()
			FOO.cmds[cmd_]()
			gg.final()
		else
			log.error("auth error1:" .. (cmd_ or 'nil') )
		end

	else
		log.error("action error:" .. (cmd_ or 'nil') )
	end

	--log.debug( "headers2= " .. table.tostring(ngx.resp.get_headers()) );
end




-----------------------------通用接口----------------
---POST set_kv  get_kv  hlen  hget_all  del hkeys
FOO.cmds.comm = function()
	log.debug( "call dc - comm" )

	--参数在post里
	ngx.req.read_body();
	local args_ = ngx.req.get_post_args();
	uu.var_dump( ngx.ctx.m_params )

	--合并参数
	for k, v in pairs( ngx.ctx.m_params ) do
		args_[k] = v
	end

	ngx.ctx.m_params     = args_
	ngx.ctx.m_params.cmd = ngx.ctx.m_params.func   --顶掉comm

	if  ns_auth_checker.check_data_cache_khv_auth( args_ ) ~= 0 then
		return    ---check dc khv fail
	end

	uu.var_dump( ngx.ctx.m_params )

	if  FOO.cmds[ ngx.ctx.m_params.cmd ] then
		log.debug( "call dc - " .. ngx.ctx.m_params.cmd )
		FOO.cmds[ ngx.ctx.m_params.cmd ]()
	else
		log.error("comm action error:" .. (ngx.ctx.m_params.cmd or 'nil') )
	end

end


--GET 用来测试的接口
FOO.cmds.comm2 = function()
	log.debug( "call dc - comm2" )
	--参数在get里
	ngx.ctx.m_params.cmd = ngx.ctx.m_params.func   --顶掉comm

	uu.var_dump( ngx.ctx.m_params )

	if  FOO.cmds[ ngx.ctx.m_params.cmd ] then
		log.debug( "call dc - " .. ngx.ctx.m_params.cmd )
		FOO.cmds[ ngx.ctx.m_params.cmd ]()
	end
end



-----------------------------------------------------------------
--存储数据
FOO.cmds.save = function()

	local name_   = ngx.ctx.m_params.name      --redis name: ma map posting
	local key_    = ngx.ctx.m_params.key
	local hash_   = ngx.ctx.m_params.hash
	local select_ = FOO.private_get_select()

	if  not ( name_ and select_ and key_ and hash_ ) then
		ngx.status = ns_const_vars.ERROR_CODE.PARAM
		uu.return_table( { ret=1, msg="PARAM_ERROR" } )
		return
	end


	local value_
	if  ngx.ctx.m_params.value then
		value_ = ngx.ctx.m_params.value   --type is string
	else
		--post
		ngx.req.read_body();
		value_ = ngx.req.get_body_data()
	end

	if  value_ then
		---if len
		if  ngx.ctx.m_params.len then
			local len_ = tonumber( ngx.ctx.m_params.len ) or -1
			if  len_ == #value_ then
				--log.day_list_simple( "redis", "dc_client", "len_ok", len_ )
			else
				log.day_list_simple( "redis", "dc_client", "error_len", key_, hash_, select_, len_, #value_, value_ )
				--ngx.status = ret_code_
				uu.return_table( { ret=1, msg="ERROR_LEN" } )
				return
			end
		end

		---if decode base64
		if  ngx.ctx.m_params.isbase64 == '1' then
			local value_64_ = ngx.decode_base64(value_)
			if  value_64_ then
				--log.day_list_simple( "redis", "dc_client", "base64_decode_ok", #value_, #value_64_ )
				value_ = value_64_
			else
				log.day_list_simple( "redis", "dc_client", "error_base64", key_, hash_, select_, value_ )
				uu.return_table( { ret=1, msg="ERROR_BASE64" } )
				return
			end
		end

		---if decode lua table
		if  ngx.ctx.m_params.islua == '1' then
			local trace_back_ = function( errmsg )
				log.day_list_simple("redis", "trace_back", errmsg, debug.traceback());
			end
			local t_
			local function decode_table_()
				t_ = table.loadstring( value_ )
			end
			local ok, errmsg = xpcall(decode_table_, trace_back_)
			if  not ok then
				log.day_list_simple( "redis", "xpcall", uu.to_str(ok), uu.to_str(errmsg) )
			end

			if  t_ then
				--log.day_list_simple( "redis", "dc_client", "table_ok", #value_ )
			else
				log.day_list_simple( "redis", "dc_client", "error_table",  key_, hash_, select_, value_ )
				uu.return_table( { ret=1, msg="ERROR_LUA_TABLE" } )
				return
			end
		end

	else
		--- error no value
		log.day_list_simple( "redis", "dc_client", "no_value", key_, hash_, select_ )
		ngx.status = ns_const_vars.ERROR_CODE.PARAM
		uu.return_table( { ret=1, msg="NO_VALUE" } )
		return
	end

	local redis_mgr_ = gg.redisMgrDcList[ name_ ]
	if  redis_mgr_ then
		-- _M.save = function( self, redis_select, key, hash, value, redis_delay_, read_only_ )
		local ret_code_, ret_ = redis_mgr_:API_redis_save( select_, key_, hash_, value_,
				ngx.ctx.m_params.redis_delay, ngx.ctx.m_params.read_only )
		if  ret_code_ == 0 then
			uu.return_table( { ret=0, data=ret_ } )  --succ
		else
			uu.return_table( { ret=ret_code_, msg=ret_ } )
		end
	else
		log.error( "conf name not exit=" .. name_ )
		ngx.status = ns_const_vars.ERROR_CODE.PARAM
		uu.return_table( { ret=1, msg="PARAM_ERROR" } )
	end

end


-----------------------------------------------------------------
--获取数据
FOO.cmds.read = function()
	local name_   = ngx.ctx.m_params.name      --redis name: ma map posting
	local key_    = ngx.ctx.m_params.key
	local hash_   = ngx.ctx.m_params.hash
	local select_ = FOO.private_get_select()

	if  ngx.ctx.m_params.no_cache == 'y' then
		--log.day_list_short( "redis", "read_no_shared_cache", name_, select_, key_, hash_ );
		ngx.ctx.no_shared_cache = 'y'   --不存shared缓存
	end

	if  not ( name_ and select_ and key_ and hash_ ) then
		ngx.status = ns_const_vars.ERROR_CODE.PARAM
		uu.return_table( { ret=1, msg="PARAM_ERROR" } )
		return
	end

	local redis_mgr_ = gg.redisMgrDcList[ name_ ]
	if  redis_mgr_ then
		--_M.read = function( self, redis_select, key, hash, read_only_ )
		local ret_code_, ret_ = redis_mgr_:API_redis_read( select_, key_, hash_, ngx.ctx.m_params.read_only )
		if  ret_code_ == 0 then
			---succ

			if  FOO.DOUBLE_CHECK_RET == 1 then
				-- 对返回的字符串进行二次检查
				local ret_ = ns_comm_gg.check_ret_string_kh( ret_, key_, hash_, name_, 'redis' )
				if  ret_ == 1 then
					ngx.status = ns_const_vars.ERROR_CODE.ERROR_RET_CHECK
					uu.return_table( { ret=1, msg="ERROR_KH" } )
					redis_mgr_:API_redis_clean_shared_cache( select_, key_, hash_ )
					return
				end
			end

			uu.return_table( { ret=0, data=ret_ } )

			if  #ret_ <= 0 then
				log.day_list_simple( "redis", "error_read_len0", select_, key_, hash_, ret_ )
			end
		else
			---fail
			uu.return_table( { ret=ret_code_, msg=ret_ } )
		end

	else
		log.error( "conf not exit=" .. name_ )
		ngx.status = ns_const_vars.ERROR_CODE.PARAM
		uu.return_table( { ret=1, msg="PARAM_ERROR" } )
	end

end



--批量获得数据
FOO.cmds.mread = function()
	local name_   = ngx.ctx.m_params.name      --redis name: ma map posting
	local select_ = FOO.private_get_select()

	if  not ( name_ and select_ ) then
		ngx.status = ns_const_vars.ERROR_CODE.PARAM
		uu.return_table( { ret=1, msg="PARAM_ERROR" } )
		return
	end

	--local key_hash_ = all_kv_list[i].key .. "_h" .. all_kv_list[i].hash
	local all_kv_list
	if  ngx.ctx.m_params.kv_list then
		local t_ = uu.split( ngx.ctx.m_params.kv_list, "-" )
		if  #t_ > 0 then
			all_kv_list = {}
			for i=1, #t_, 2 do
				all_kv_list[ #all_kv_list + 1 ] = { key=t_[i],  hash=t_[i+1] }
			end
		end
		uu.var_dump( all_kv_list )
	else
		--read post
		ngx.req.read_body();
		local args = ngx.req.get_post_args();
		uu.var_dump( args )
		if  args['kv_list'] then
			all_kv_list = uu.safe_loadstring ( args['kv_list'] )
		end
	end

	if (not  all_kv_list) or #all_kv_list <= 0 then
		ngx.status = ns_const_vars.ERROR_CODE.PARAM
		uu.return_table( { ret=1, msg="PARAM_ERROR_LIST0" } )
		return
	end

	local redis_mgr_ = gg.redisMgrDcList[ name_ ]
	if  redis_mgr_ then
		---_M.mread = function( self, redis_select, all_kv_list )
		local ret_code_, ret_ = redis_mgr_:API_redis_mread( select_, all_kv_list )
		uu.var_dump( ret_ )
		if  ret_code_ == 0 then
			uu.return_table( { ret=0, data=ret_ } )  --succ
		else
			uu.return_table( { ret=ret_code_, msg=ret_ } )
		end
	else
		log.error( "conf not exit=" .. name_ )
		ngx.status = ns_const_vars.ERROR_CODE.PARAM
		uu.return_table( { ret=1, msg="PARAM_ERROR" } )
	end

end



-- 删除一个key下的一个hash  对应 redis hdel
-- 成功： 返回被删除的个数
FOO.cmds.del = function()
	local name_   = ngx.ctx.m_params.name      --redis name: ma map posting
	local key_    = ngx.ctx.m_params.key
	local hash_   = ngx.ctx.m_params.hash
	local select_ = FOO.private_get_select()

	if  not ( name_ and select_ and key_ and hash_ ) then
		ngx.status = ns_const_vars.ERROR_CODE.PARAM
		uu.return_table( { ret=1, msg="PARAM_ERROR" } )
		return
	end

	local redis_mgr_ = gg.redisMgrDcList[ name_ ]
	if  redis_mgr_ then
		---_M.del = function( self, redis_select, key, hash )
		local ret_code_, ret_ = redis_mgr_:API_redis_del( select_, key_, hash_ )
		if  ret_code_ == 0 then
			uu.return_table( { ret=0, data=ret_ } )  ---被删除个数
		else
			uu.return_table( { ret=ret_code_, msg=ret_ } )
		end
	else
		log.error( "conf not exit=" .. name_ )
		ngx.status = ns_const_vars.ERROR_CODE.PARAM
		uu.return_table( { ret=1, msg="PARAM_ERROR" } )
	end

end

FOO.cmds.hdel = FOO.cmds.del    ---别名



-- 一个key下有多少个hash值
FOO.cmds.hlen = function()
	local name_   = ngx.ctx.m_params.name      --redis name: ma map posting
	local key_    = ngx.ctx.m_params.key
	local select_ = FOO.private_get_select()
	if  not ( name_ and select_ and key_ ) then
		ngx.status = ns_const_vars.ERROR_CODE.PARAM
		uu.return_table( { ret=1, msg="PARAM_ERROR" } )
		return
	end

	local redis_mgr_ = gg.redisMgrDcList[ name_ ]
	if  redis_mgr_ then
		---_M.hlen = function( self, redis_select, key )
		local ret_code_, ret_ = redis_mgr_:API_redis_hlen( select_, key_ )
		if  ret_code_ == 0 then
			uu.return_table( { ret=0,         data=ret_ } )
		else
			uu.return_table( { ret=ret_code_, msg=ret_ } )
		end
	else
		log.error( "conf not exit=" .. name_ )
		ngx.status = ns_const_vars.ERROR_CODE.PARAM
		uu.return_table( { ret=1, msg="PARAM_ERROR" } )
	end

end


-- 获得指定key下的所有hash的键值
FOO.cmds.hkeys = function()
	local name_   = ngx.ctx.m_params.name      --redis name: ma map posting
	local key_    = ngx.ctx.m_params.key
	local select_ = FOO.private_get_select()
	if  not ( name_ and select_ and key_ ) then
		ngx.status = ns_const_vars.ERROR_CODE.PARAM
		uu.return_table( { ret=1, msg="PARAM_ERROR" } )
		return
	end

	local redis_mgr_ = gg.redisMgrDcList[ name_ ]
	if  redis_mgr_ then
		---_M.hkeys = function( self, redis_select, key_ )
		local ret_code_, ret_ = redis_mgr_:API_redis_hkeys( select_, key_ )
		if  ret_code_ == 0 then
			uu.return_table( { ret=0,         data=ret_ } )
		else
			uu.return_table( { ret=ret_code_, msg=ret_ } )
		end
	else
		log.error( "conf not exit=" .. name_ )
		ngx.status = ns_const_vars.ERROR_CODE.PARAM
		uu.return_table( { ret=1, msg="PARAM_ERROR" } )
	end

end



--获得一个key下的所有值
FOO.cmds.hget_all = function()
	local name_   = ngx.ctx.m_params.name      --redis name: ma map posting
	local key_    = ngx.ctx.m_params.key
	local select_ = FOO.private_get_select()

	if  not ( name_ and select_ and key_ ) then
		ngx.status = ns_const_vars.ERROR_CODE.PARAM
		uu.return_table( { ret=1, msg="PARAM_ERROR" } )
		return
	end

	local redis_mgr_ = gg.redisMgrDcList[ name_ ]
	if  redis_mgr_ then
		---_M.hget_all = function( self, redis_select, key )
		local ret_code_, ret_ = redis_mgr_:API_redis_hget_all( select_, key_ )
		if  ret_code_ == 0 then
			uu.return_table( { ret=0,         data=ret_ } )
		else
			uu.return_table( { ret=ret_code_, msg=ret_ } )
		end
	else
		log.error( "conf not exit=" .. name_ )
		ngx.status = ns_const_vars.ERROR_CODE.PARAM
		uu.return_table( { ret=1, msg="PARAM_ERROR" } )
	end

end

FOO.cmds.hgetall = FOO.cmds.hget_all    ---别名



---分段扫描使用(从0开始，每一次传入上次返回的游标值)
FOO.cmds.scan = function()
    local name_   = ngx.ctx.m_params.name      --redis name: ma map posting
    local cursor_ = ngx.ctx.m_params.cursor
    local select_ = FOO.private_get_select()

    if  not ( name_ and select_ and cursor_ ) then
		ngx.status = ns_const_vars.ERROR_CODE.PARAM
		uu.return_table( { ret=1, msg="PARAM_ERROR" } )
        return
    end

    local redis_mgr_ = gg.redisMgrDcList[ name_ ]
    if  redis_mgr_ then
		--- _M.scan = function( self, redis_select, cursor_ )
        local ret_code_, ret_ = redis_mgr_:API_redis_scan( select_, cursor_ )
		if  ret_code_ == 0 then
			uu.return_table( { ret=0,         data=ret_ } )
		else
			uu.return_table( { ret=ret_code_, msg=ret_ } )
		end
    else
        log.error( "conf not exit=" .. name_ )
		ngx.status = ns_const_vars.ERROR_CODE.PARAM
		uu.return_table( { ret=1, msg="PARAM_ERROR" } )
    end
end



--清理缓存，清理后会重新拉取redis
FOO.cmds.clean_cache = function()
	local name_   = ngx.ctx.m_params.name      --redis name: ma map posting
	local key_    = ngx.ctx.m_params.key
	local hash_   = ngx.ctx.m_params.hash
	local select_ = FOO.private_get_select()

	if  not ( name_ and select_ and key_ and hash_ ) then
		ngx.status = ns_const_vars.ERROR_CODE.PARAM
		uu.return_table( { ret=1, msg="PARAM_ERROR" } )
		return
	end

	local redis_mgr_ = gg.redisMgrDcList[ name_ ]
	if  redis_mgr_ then
		---_M.clean_shared_cache_redis = function(self, select_, key_, hash_ )
		redis_mgr_:API_redis_clean_shared_cache( select_, key_, hash_ )
		uu.return_table( { ret=0 } )
	else
		log.error( "conf not exit=" .. name_ )
		ngx.status = ns_const_vars.ERROR_CODE.PARAM
		uu.return_table( { ret=1, msg="PARAM_ERROR" } )
	end
end



---------------------------------kv 独立库
FOO.cmds.set_kv = function()

	local name_   = ngx.ctx.m_params.name      --redis name: ma map posting
	local key_    = ngx.ctx.m_params.key
	local select_ = FOO.private_get_select()

	local value_  = ngx.ctx.m_params.value

	local et_
	if  ngx.ctx.m_params.et then
		et_ = tonumber(ngx.ctx.m_params.et)
	end

	if  not ( name_ and select_ and key_ and value_ ) then
		ngx.status = ns_const_vars.ERROR_CODE.PARAM
		uu.return_table( { ret=1, msg="PARAM_ERROR" } )
		return
	end

	local redis_mgr_ = gg.redisMgrDcList[ name_ ]
	if  redis_mgr_ then
		---_M.set_kv = function( self, redis_select, key, value, et )
		local ret_code_, ret_ = redis_mgr_:API_redis_set_kv( select_, key_, value_, et_ )
		if  ret_code_ == 0 then
			uu.return_table( { ret=0, data=ret_ } )  --succ
		else
			uu.return_table( { ret=ret_code_, msg=ret_ } )
		end
	else
		log.error( "conf not exit=" .. name_ )
		ngx.status = ns_const_vars.ERROR_CODE.PARAM
		uu.return_table( { ret=1, msg="PARAM_ERROR" } )
	end

end



FOO.cmds.get_kv = function()
	local name_   = ngx.ctx.m_params.name      --redis name: ma map posting
	local key_    = ngx.ctx.m_params.key
	local select_ = FOO.private_get_select()

	if  not ( name_ and select_ and key_ ) then
		ngx.status = ns_const_vars.ERROR_CODE.PARAM
		uu.return_table( { ret=1, msg="PARAM_ERROR" } )
		return
	end

	local redis_mgr_ = gg.redisMgrDcList[ name_ ]
	if  redis_mgr_ then
		---_M.get_kv = function( self, redis_select, key )
		local ret_code_, ret_ = redis_mgr_:API_redis_get_kv( select_, key_ )
		if  ret_code_ == 0 then
			uu.return_table( { ret=0, data=ret_ } )  --succ
		else
			uu.return_table( { ret=ret_code_, msg=ret_ } )
		end
	else
		log.error( "conf not exit=" .. name_ )
		ngx.status = ns_const_vars.ERROR_CODE.PARAM
		uu.return_table( { ret=1, msg="PARAM_ERROR" } )
	end

end


--尝试锁一个远程key_hash锁
--返回: 0=成功   其他=失败（一般是超时）
FOO.cmds.try_lock_kh = function()
	local args_   = ngx.ctx.m_params
	local name_   = args_.name               --redis name: ma map posting
	local select_ = FOO.private_get_select()

	local kh_
	if  args_.kh then
		kh_     = args_.kh
	elseif  args_.key and  args_.hash then
		kh_     = args_.key .. '_h' .. args_.hash      --key .. "_h" .. hash
	end

	if  not ( name_ and select_ and kh_ ) then
		ngx.status = ns_const_vars.ERROR_CODE.PARAM
		uu.return_table( { ret=1, msg="PARAM_ERROR" } )
		return
	end

	local redis_mgr_ = gg.redisMgrDcList[ name_ ]
	if  redis_mgr_ then
		---_M.check_redis_resty_lock = function( self, redis_select, key_hash_ )
		local ret_, err_ = redis_mgr_:API_redis_try_get_resty_lock( select_, kh_ )
		if  err_ then
			uu.return_table( { ret=ret_, msg=err_ } )
		else
			uu.return_table( { ret=0,    lock_kh_id=ret_ } )
		end
	else
		log.error( "conf not exit=" .. name_ )
		ngx.status = ns_const_vars.ERROR_CODE.PARAM
		uu.return_table( { ret=1, msg="PARAM_ERROR" } )
	end
end


-- 跟其他dc模块一样名字的别名
FOO.cmds.lock_kh = FOO.cmds.try_lock_kh


--解锁
--返回值:  0=成功   not_lock=未发现有锁(已被解开)   其他=系统错误
FOO.cmds.unlock_kh = function()
	local args_   = ngx.ctx.m_params
	local name_   = args_.name      --redis name: ma map posting
	local select_ = FOO.private_get_select()

	local kh_
	if  args_.kh then
		kh_     = args_.kh
	elseif  args_.key and  args_.hash then
		kh_     = args_.key .. '_h' .. args_.hash      --key .. "_h" .. hash
	end

	if  not ( name_ and select_ and kh_ ) then
		ngx.status = ns_const_vars.ERROR_CODE.PARAM
		uu.return_table( { ret=1, msg="PARAM_ERROR" } )
		return
	end

	local redis_mgr_ = gg.redisMgrDcList[ name_ ]
	if  redis_mgr_ then
		--- _M.unlock_resty_lock = function( self, redis_select, key_hash_, read_only_ )
		local ret_code_ = redis_mgr_:API_redis_unlock_resty_lock( select_, kh_ )
		uu.return_table( { ret=0, code=ret_code_ } )
	else
		log.error( "conf not exit=" .. name_ )
		ngx.status = ns_const_vars.ERROR_CODE.PARAM
		uu.return_table( { ret=1, msg="PARAM_ERROR" } )
	end

end



-------------------------------------------private
FOO.private_get_select = function()
	local select_
	if  ngx.ctx.m_params.select then
		select_ = tonumber(ngx.ctx.m_params.select)   --数字
		if  not select_ then
			select_ = ngx.ctx.m_params.select         --comm
		end
	end
	return select_
end


------------初始化
FOO.init = function()

	---定时检查share_dict剩余空间
	local shared_dict_name_list_ = { "miniw", "ssdb", }
	for i=0, 9 do
		shared_dict_name_list_[ #shared_dict_name_list_ + 1 ] = "ssdb"  .. i
		shared_dict_name_list_[ #shared_dict_name_list_ + 1 ] = "miniw" .. i
	end

	if  ns_config.redis then
		for k, v in pairs( ns_config.redis ) do
			for i=0, 9 do
				if  type(k) == 'string' then
					shared_dict_name_list_[ #shared_dict_name_list_ + 1 ] = k .. i
				end
			end
		end
	end
	ns_share_dict_checker.start_check_by_timer( shared_dict_name_list_, 603, "dc3" )

end


FOO.init()
return FOO;
